package com.fzy.core;

import java.io.IOException;
import java.io.LineNumberReader;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;

import com.fzy.core.utils.StringUtils;

public class DataLoader {
	private static final Log log = LogFactory.getLog(DataLoader.class);
	
	private static String DEFAULT_COMMENT_PREFIX = "--";
	DataSource dataSource;
	
	private String sqlScriptEncoding = "UTF-8";
	
	private Resource[] scripts;
	
	private String commentPrefix = DEFAULT_COMMENT_PREFIX;
	
	public void setDataSource(DataSource dataSource){
		this.dataSource = dataSource;
	}
	public void setCommentPrefix(String commentPrefix) {
		this.commentPrefix = commentPrefix;
	}
	public void setScript(Resource script) {
		this.scripts = new Resource[] {script};
	}

	/**
	 * Set locations of properties files to be loaded.
	 * <p>Can point to classic properties files or to XML files
	 * that follow JDK 1.5's properties XML format.
	 */
	public void setScripts(Resource[] scripts) {
		this.scripts = scripts;
	}
	public Connection getConnection() {
		Connection jdbcConnection = null;

		return jdbcConnection;
	}
	
	public void populate() throws SQLException {
		Connection connection = dataSource.getConnection();
		if(scripts != null)
		for (int i = 0; i < scripts.length; i++) {
			Resource script = scripts[i];
			executeSqlScript(connection, applyEncodingIfNecessary(script), true, true);
		}
	}
	
	private EncodedResource applyEncodingIfNecessary(Resource script) {
		if (script instanceof EncodedResource) {
			return (EncodedResource) script;
		}
		else {
			return new EncodedResource(script, this.sqlScriptEncoding);
		}
	}
	
	private void executeSqlScript(Connection connection, EncodedResource resource,
			boolean continueOnError, boolean ignoreFailedDrops) throws SQLException {

		long startTime = System.currentTimeMillis();
		List statements = new LinkedList();
		String script;
		try {
			script = readScript(resource);
		}
		catch (IOException ex) {
			return;
		}
		char delimiter = ';';
		if (!containsSqlScriptDelimiters(script, delimiter)) {
			delimiter = '\n';
		}
		splitSqlScript(script, delimiter, statements);
		int lineNumber = 0;
		Statement stmt = connection.createStatement();
		try {
			for (Iterator iterator = statements.iterator(); iterator.hasNext();) {
				String statement = (String) iterator.next();
				lineNumber++;
				try {
					int rowsAffected = stmt.executeUpdate(statement);
				}
				catch (SQLException ex) {
					boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
					if (continueOnError || (dropStatement && ignoreFailedDrops)) {
						if (log.isDebugEnabled()) {
							log.debug("Failed to execute SQL script statement at line " + lineNumber +
									" of resource " + resource + ": " + statement, ex);
						}
					}
					else {
						log.error("other errors");
					}
				}
			}
		}
		finally {
			try {
				stmt.close();
			}
			catch (Throwable ex) {
				log.debug("Could not close JDBC Statement", ex);
			}
		}
		long elapsedTime = System.currentTimeMillis() - startTime;

	}
	private String readScript(EncodedResource resource) throws IOException {
		LineNumberReader lnr = new LineNumberReader(resource.getReader());
		String currentStatement = lnr.readLine();
		StringBuffer scriptBuffer = new StringBuffer();
		while (currentStatement != null) {
			if (StringUtils.hasText(currentStatement) &&
					(this.commentPrefix != null && !currentStatement.startsWith(this.commentPrefix))) {
				if (scriptBuffer.length() > 0) {
					scriptBuffer.append('\n');
				}
				scriptBuffer.append(currentStatement);
			}
			currentStatement = lnr.readLine();
		}
		return scriptBuffer.toString();
	}

	/**
	 * Does the provided SQL script contain the specified delimiter?
	 * @param script the SQL script
	 * @param delim character delimiting each statement - typically a ';' character
	 */
	private boolean containsSqlScriptDelimiters(String script, char delim) {
		boolean inLiteral = false;
		char[] content = script.toCharArray();
		for (int i = 0; i < script.length(); i++) {
			if (content[i] == '\'') {
				inLiteral = !inLiteral;
			}
			if (content[i] == delim && !inLiteral) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Split an SQL script into separate statements delimited with the provided delimiter character.
	 * Each individual statement will be added to the provided <code>List</code>.
	 * @param script the SQL script
	 * @param delim character delimiting each statement (typically a ';' character)
	 * @param statements the List that will contain the individual statements
	 */
	private void splitSqlScript(String script, char delim, List statements) {
		StringBuffer sb = new StringBuffer();
		boolean inLiteral = false;
		char[] content = script.toCharArray();
		for (int i = 0; i < script.length(); i++) {
			char c = content[i];
			if (c == '\'') {
				inLiteral = !inLiteral;
			}
			if (!inLiteral) {
				if (c == delim) {
					if (sb.length() > 0) {
						statements.add(sb.toString());
						sb = new StringBuffer();
					}
					continue;
				}
				else if (c == '\n' || c == '\t') {
					c = ' ';
				}
			}
			sb.append(c);
		}
		if (StringUtils.hasText(sb)) {
			statements.add(sb.toString());
		}
	}


	private Integer getSequenceNextVal(String sequenceName)
			throws SQLException, Exception {
		Statement st = getConnection().createStatement();
		ResultSet rs = st.executeQuery("SELECT " + sequenceName
				+ ".nextval FROM dual");
		rs.next();
		st = null;
		return new Integer(rs.getInt(1));
	}

}
